目录
  1. 1. 公式推导
  2. 2. 最终代码
  3. 3. 向量化技术⭐️
    1. 3.1. 1. 改造公式
    2. 3.2. 2. 变量维度说明:
    3. 3.3. 3. 列向量改矩阵⭐️
  4. 4. 对于一些细节的补充:
Matlab编程-- 向量化技术&似然概率

Matlab编程-- 向量化技术&似然概率

本篇博客的目标:对数似然概率公式的Matlab编程

对数似然概率公式:

lnp(Xμ,Σ)=12(Dln(2π)+lnΣ+(xnμ)TΣ1(xnμ))\begin{aligned} \ln p(\mathbf{X} | \boldsymbol{\mu}, \mathbf{\Sigma})= -\frac{1}{2}(Dln(2\pi)+ln|\Sigma|+\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)^{T} \mathbf{\Sigma}^{-1}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)) \end{aligned}

编程难点:公式中的xx是列向量,而Matlab中的XX是矩阵,为了提高代码的效率避免使用过多的forfor语句,这里使用向量化技术完成算法的编程任务。

公式推导

参考链接:https://www.cnblogs.com/ccienfall/p/6049021.html

多维高斯函数的似然概率:

N(xμ,Σ)=1(2π)D/21Σ1/2exp{12(xμ)TΣ1(xμ)}\mathcal{N}(\mathbf{x} | \boldsymbol{\mu}, \mathbf{\Sigma})=\frac{1}{(2 \pi)^{D / 2}} \frac{1}{|\mathbf{\Sigma}|^{1 / 2}} \exp \left\{-\frac{1}{2}(\mathbf{x}-\boldsymbol{\mu})^{T} \mathbf{\Sigma}^{-1}(\mathbf{x}-\boldsymbol{\mu})\right\}

若是考虑数据集X=(x1,,xN)T\mathbf{X}=\left(\mathbf{x}_{1}, \ldots, \mathbf{x}_{N}\right)^{T},再求对数则为:

lnp(Xμ,Σ)=ND2ln(2π)N2lnΣ12n=1N(xnμ)TΣ1(xnμ)\ln p(\mathbf{X} | \boldsymbol{\mu}, \mathbf{\Sigma})=-\frac{N D}{2} \ln (2 \pi)-\frac{N}{2} \ln |\mathbf{\Sigma}|-\frac{1}{2} \sum_{n=1}^{N}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)^{T} \mathbf{\Sigma}^{-1}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)

⭐️但是我需要的似然概率,xix_i在第kk个分布下的似然概率:【实数】

N(xiμk,Σk)=1(2π)D/21Σk1/2exp{12(xiμk)TΣk1(xiμk)}\mathcal{N}(\mathbf{x_i} | \boldsymbol{\mu_k}, \mathbf{\Sigma_k})=\frac{1}{(2 \pi)^{D / 2}} \frac{1}{|\mathbf{\Sigma_k}|^{1 / 2}} \exp \left\{-\frac{1}{2}(\mathbf{x_i}-\boldsymbol{\mu_k})^{T} \mathbf{\Sigma_k}^{-1}(\mathbf{x_i}-\boldsymbol{\mu_k})\right\}

展开后,得证:

lnp(Xμ,Σ)=D2ln(2π)12lnΣ12(xnμ)TΣ1(xnμ)=12(Dln(2π)+lnΣ+(xnμ)TΣ1(xnμ))\begin{aligned} \ln p(\mathbf{X} | \boldsymbol{\mu}, \mathbf{\Sigma})= &-\frac{ D}{2} \ln (2 \pi)-\frac{1}{2} \ln |\mathbf{\Sigma}|-\frac{1}{2} \left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)^{T} \mathbf{\Sigma}^{-1}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right) \\ = & -\frac{1}{2}(Dln(2\pi)+ln|\Sigma|+\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)^{T} \mathbf{\Sigma}^{-1}\left(\mathbf{x}_{\mathbf{n}}-\boldsymbol{\mu}\right)) \end{aligned}

最终代码

function logprob = lgmmprob(data, mu, sigma, w)
% 计算GMM的观察概率(似然概率)
ndim = size(data, 1);
C = sum(mu.*mu./sigma) + sum(log(sigma));
D = (1./sigma)' * (data .* data) - 2 * (mu./sigma)' * data + ndim * log(2 * pi);
logprob = -0.5 * (bsxfun(@plus, C', D));
logprob = bsxfun(@plus, logprob, log(w));

舍弃系数-0.5,进一步简化需要编程的部分:

Dln(2π)+lnΣ+(xiμk)TΣk1(xiμk)Dln(2\pi)+ln|\Sigma|+\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)^{T} \mathbf{\Sigma_k}^{-1}\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)

需要说明的是上面所有的小项都是实数。

向量化技术⭐️

1. 改造公式

Σ\Sigma在数学推导中的公式中是 fullvariancefull -variance 矩阵,但是一般代码中的协方差矩阵默认是对角协方差矩阵。故这里就存在一个算法和实际编程中的一个不匹配的地方,故这个时候需要对原本的算法公式进行改造

  • 正常的推到方程

(a1,a2)(Δ100Δ2)(a1a2)=Δ1a12+Δ2a22\left(a_{1}, a_{2}\right)*\left(\begin{array}{cc} {\Delta_{1}} & {0} \\ {0} & {\Delta_{2}} \end{array}\right)*\left(\begin{array}{l} {a_{1}} \\ {a_{2}} \end{array}\right)=\Delta_{1} a_{1}^{2}+\Delta_{2} a_{2}^{2}

  • 代码中待编程的方程

(Δ1,Δ2)(a1a2).(a1a2)=Δ1a12+Δ2a22\left(\Delta_{1}, \Delta_{2}\right)*\left(\begin{array}{l} {a_{1}} \\ {a_{2}} \end{array}\right).*\left(\begin{array}{l} {a_{1}} \\ {a_{2}} \end{array}\right)=\Delta_{1} a_{1}^{2}+\Delta_{2} a_{2}^{2}

故原先的第三项为:(xiμk)TΣk1(xiμk)\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)^{T} \mathbf{\Sigma_k}^{-1}\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)

其中令Σ\Sigma是一个列向量,可以见下节的变量维度说明(Δ1,Δ2)=(Σk1)T(\Delta_1,\Delta_2)=(\Sigma_{k}^{-1})^T

(Σk1)T(xiμk).(xiμk)(\mathbf{\Sigma_k}^{-1})^T*\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right).*\left(\mathbf{x}_i-\boldsymbol{\mu_k}\right)

  • 本例中,代码所需编写的方程为:

Dln(2π)+lnΣk+(Σk1)T[xi . xi2μk . xi+μk . μk]Dln(2\pi)+ln|\Sigma_k|+(\mathbf{\Sigma_k}^{-1})^T*[x_i\ .*\ x_i-2\mu_k\ .*\ x_i+\mu_k\ .*\ \mu_k]

2. 变量维度说明:

xi:(p,1)x_i:(p,1)

X={x1,x2,,xi,,xn}:(p,n)X=\{x_1,x_2,\dots,x_i,\dots,x_n\}:(p,n)

Σk:(p,1)\Sigma_k:(p,1)

Σ={Σ1,Σ2,,Σk,,ΣK}:(p,K)\Sigma = \{\Sigma_1,\Sigma_2,\dots,\Sigma_k,\dots,\Sigma_K\}:(p,K)

μk:(p,1)\mu_k:(p,1)

μ={μ1,μ2,,μk,,μK}:(p,K)\mu=\{\mu_1,\mu_2,\dots,\mu_k,\dots,\mu_K\}:(p,K)

3. 列向量改矩阵⭐️

这个是最关键的一步,一步一步解答,以便下次遇到的时候可以轻松应对,上式是原数学方程式中的数学向量,但为了编程,需要向量化。

  • 首先明确向量化的目的:用Σ,μ,X\Sigma,\mu,X代替Σk,μk,xi\Sigma_k,\mu_k,x_i
  • 其次明确维度:每一项的都是实数

这里再明确一下待编程的方程为:

Dln(2π)+lnΣk+(Σk1)T[xi . xi2μk . xi+μk . μk]Dln(2\pi)+ln|\Sigma_k|+(\mathbf{\Sigma_k}^{-1})^T*[x_i\ .*\ x_i-2\mu_k\ .*\ x_i+\mu_k\ .*\ \mu_k]

根据每一小项是否与分布kk和样本ii有关可进一步划分:

与k有关 与k无关
ii有关 1. k与i 矩阵乘
2. k 与 i 点乘,需转换为矩阵乘
3. 不满足以上两个条件
ii无关 lnΣk1ln\|\Sigma_k^{-1}\| Dln(2π)Dln(2\pi)
  1. 情况一:与kkii都无关的项,Dln(2π):Dln(2\pi):实数
ndim * log(2 * pi)
  1. 情况二:仅与kk有关的项,lnΣk1:ln|\Sigma_k^{-1}|: 实数

    这里试求方差的行列式,又方差是对角矩阵,所以可简化为:

ln(Δ100Δ2)=lnΔ1Δ2=lnΔ1+lnΔ2=sum(ln(Δ1,Δ2))ln|\left(\begin{array}{cc} {\Delta_{1}} & {0} \\ {0} & {\Delta_{2}} \end{array}\right)|=ln|\Delta_1\cdot\Delta_2|=ln\Delta_1+ln\Delta_2=sum(ln(\Delta_1,\Delta_2))

lnΣk=sum(Σk):(1,1)ln|\Sigma_k|=sum(\Sigma_k):(1,1)

​ 可以向右扩展:

{sum(Σ1),sum(Σ2),,sum(Σk)}=sum(Σ):(1,k)\{sum(\Sigma_1),sum(\Sigma_2),\dots,sum(\Sigma_k)\}=sum(\Sigma):(1,k)

  1. 情况三:kkii已经分开,且由矩阵乘相连

    (Σk1)T[xi . xi](\mathbf{\Sigma_k}^{-1})^T*[x_i\ .*\ x_i](p,1)T(p,1).(p,1)=(1,1)(p,1)^T*(p,1).*(p,1)=(1,1)实数

    向量化具体细节讲解:

    元素向量化:将xix_i横向扩展,将Σk\Sigma_k纵向扩展

{(Σk1)Tx1.x1,(Σk1)Tx2.x2,,(Σk1)Txn.xn}=(Σk1)T{x1.x1, x2.x2, ,xn.xn}=(Σk1)T(X.X):(1,p)(p,n)=(1,n)=((Σ11,Σ21,,ΣK1))T(X.X)=(Σ1)T(X.X):(k,n)\{(\mathbf{\Sigma_k}^{-1})^Tx_1.*x_1,(\mathbf{\Sigma_k}^{-1})^Tx_2.*x_2,\dots,(\mathbf{\Sigma_k}^{-1})^Tx_n.*x_n\} \\ = (\mathbf{\Sigma_k}^{-1})^T *\{x_1.*x_1,\ x_2.*x_2,\ \dots,x_n.*x_n\} \\ =(\mathbf{\Sigma_k}^{-1})^T*(X.*X):(1,p)*(p,n)=(1,n)还可以向上下扩展\\ =((\mathbf{\Sigma_1}^{-1},\mathbf{\Sigma_2}^{-1},\dots,\mathbf{\Sigma_K}^{-1}))^T*(X.*X) \\=(\Sigma^{-1})^T*(X.*X):(k,n)

  1. 情况四:kkii为点乘,需要转换为矩阵乘:(Σk1)T[2μk.xi](\mathbf{\Sigma_k}^{-1})^T*[-2\mu_k.*x_i](p,1)T(p,1)=(1,1)(p,1)^T*(p,1)=(1,1)实数

但此时有个问题,无法进行向量化,因为这里的运算规则必须先进行点乘运算,再进行矩阵乘运算,若需向量化中间必须用矩阵乘相连,原因后面解释。

2(Σk1)T.μkTxi=2(Σk1.μk)Txi-2*(\Sigma_k^{-1})^T.*\mu_k^T*x_i=-2(\Sigma_k^{-1}.*\mu_k)^T*x_i

​ 向量化处理同情况三:直接去掉下标即可

2(Σ1.μ)TX-2(\Sigma^{-1}.*\mu)^T*X

  1. 情况五:都为kk,中间既有矩阵乘又有点乘

    (Σk1)Tμk . μk:(p,1)T(p,1)=(1,1)(\mathbf{\Sigma_k}^{-1})^T*\mu_k\ .*\ \mu_k:(p,1)^T(p,1)=(1,1)

    矩阵乘转换为点乘

    (Σk1)Tμk . μk=sum(Σk1.μk.μk)(\mathbf{\Sigma_k}^{-1})^T*\mu_k\ .*\ \mu_k=sum(\Sigma_k^{-1}.*\mu_k.*\mu_k)

对于一些细节的补充:

  1. 为啥必须为矩阵乘来连接两个部分,举例如下:

    若:X=(x1,x2,,xn)X=(x_1,x_2,\dots,x_n)

    (Ax1,Ax2,,Axn)=A(x1,x2,,xn)=AX(Ax_1,Ax_2,\dots,Ax_n)=A*(x_1,x_2,\dots,x_n)=A*X

    而点乘则不满足这条性质:

    (A.x1,A.x2,,A.xn)(x1,x2,,xn)=AX(A.*x_1,A.*x_2,\dots,A.*x_n)*(x_1,x_2,\dots,x_n)=A*X

  2. 为啥情况五中,不允许出现点乘?

    fifth={(Σ11)T(μ.μ1),(Σ21)T(μ2.μ2),,(Σk1)T(μk.μk)}fifth=\{(\mathbf{\Sigma_1}^{-1})^T*(\mu_.*\mu_1),(\mathbf{\Sigma_2}^{-1})^T*(\mu_2.*\mu_2),\dots,(\mathbf{\Sigma_k}^{-1})^T*(\mu_k.*\mu_k)\}

    根据向量化的定理,根本没办法凑出一个完整的矩阵如μ\mu,若直接去除下标则为:

    (Σ1)Tμ.μ:(p,k)T(p,k)=(k,k)(\Sigma^{-1})^T*\mu.*\mu:(p,k)^T(p,k)=(k,k)

    上式肯定不可能办到,因为向量化同一个小标只能往一个方向扩展,即应为(1,k)